package edu.drexel.jrexel2d.collision;

import java.util.ArrayList;

import edu.drexel.jrexel2d.gamestate.Level;
import edu.drexel.jrexel2d.objects2D.*;

public class Detector {
	private ArrayList<Object2D> objs ;
	private int backdropHeight ;
	private int nudge ;	// The threshold for how much an object will "nudge" around collisions
	
	public Detector(Level l) {
		objs = l.getObjects() ;
		backdropHeight = l.getBackdrop().getHeight() ;
		nudge = 5 ;
	}
	
	public ArrayList<Collision> check() {
		ArrayList<Collision> cols = new ArrayList<Collision>() ;
		
		for(int i=0; i<objs.size(); i++) {
			Object2D o = objs.get(i) ;
			if(o instanceof Player) checkBounds(o) ;
			for(int j=i+1; j<objs.size(); j++) {
				Collision c = check(o, objs.get(j)) ;
				if(c != null) {
					cols.add(c) ;
				}
			}
		}
		
		return cols ;
	}
	
	private Collision check(Object2D o1, Object2D o2) {
		Collision c = null ;

		Point2D p1 = o1.getBase() ;
		Point2D p2 = o2.getBase() ;
		
		int yDif = p1.getY() - p2.getY() ;
		int fullDepth = o1.getDepth() + o2.getDepth() ;
		if(Math.abs(yDif) < fullDepth) {
			boolean colliding = false ;
			
			if(o1 instanceof Player) {
				if(o2 instanceof Player) colliding = objCheck((Player) o1, (Player) o2) ;
				else if(o2 instanceof Rectangle) colliding = objCheck((Player) o1, (Rectangle) o2) ;
				else if(o2 instanceof Circle) colliding = objCheck((Player) o1, (Circle) o2) ;
				else if(o2 instanceof Polygon) colliding = objCheck((Player) o1, (Polygon) o2) ;
			}
			else if(o1 instanceof Rectangle) {
				if(o2 instanceof Player) colliding = objCheck((Player) o2, (Rectangle) o1) ;
				else if(o2 instanceof Rectangle) colliding = objCheck((Rectangle) o1, (Rectangle) o2) ;
				else if(o2 instanceof Circle) colliding = objCheck((Rectangle) o1, (Circle) o2) ;
				else if(o2 instanceof Polygon) colliding = objCheck((Rectangle) o1, (Polygon) o2) ;
			}
			else if(o1 instanceof Circle) {
				if(o2 instanceof Player) colliding = objCheck((Player) o2, (Circle) o1) ;
				else if(o2 instanceof Rectangle) colliding = objCheck((Rectangle) o2, (Circle) o1) ;
				else if(o2 instanceof Circle) colliding = objCheck((Circle) o1, (Circle) o2) ;
				else if(o2 instanceof Polygon) colliding = objCheck((Circle) o1, (Polygon) o2) ;
			}
			else if(o1 instanceof Polygon) {
				if(o2 instanceof Player) colliding = objCheck((Player) o2, (Polygon) o1) ;
				else if(o2 instanceof Rectangle) colliding = objCheck((Rectangle) o2, (Polygon) o1) ;
				else if(o2 instanceof Circle) colliding = objCheck((Circle) o2, (Polygon) o1) ;
				else if(o2 instanceof Polygon) colliding = objCheck((Polygon) o1, (Polygon) o2) ;
			}
			
			if(colliding) {
				c = new Collision(o1.getId(), o2.getId()) ;
			}
		}
		
		return c ;
	}
	
	private boolean objCheck(Player p1, Player p2) {
		Point2D po1 = p1.getLocation() ;
		Point2D po2 = p2.getLocation() ;
		
		boolean ret = (Math.abs(po1.getX() - po2.getX()) < p1.getWidth()/2 + p2.getWidth()/2) ;
		
		if(ret == true && (p1.isJumping() || p2.isJumping())) {
			ret = (Math.abs(po1.getY() - po2.getY()) < p1.getHeight()/2 + p2.getHeight()/2) ;
		}
		
		return ret ;
	}
	
	private boolean objCheck(Player p, Rectangle r) {
		Point2D po1 = p.getLocation() ;
		Point2D po2 = r.getLocation() ;
		
		boolean ret = (Math.abs(po1.getX() - po2.getX()) < p.getWidth()/2 + r.getWidth()/2) ;
		
		if(ret == true && p.isJumping()) {
			ret = (Math.abs(po1.getY() - po2.getY()) < p.getHeight()/2 + r.getHeight()/2) ;
		}
		
		return ret ;
	}
	
	private boolean objCheck(Player p, Circle c) {
		Point2D po1 = p.getLocation() ;
		Point2D po2 = c.getLocation() ;
		
		boolean ret = (Math.abs(po1.getX() - po2.getX()) < p.getWidth()/2 + c.getRadius()) ;
		
		if(ret == true && p.isJumping()) {
			ret = (Math.abs(po1.getY() - po2.getY()) < p.getHeight()/2 + c.getRadius()/2) ;
		}
		
		return ret ;
	}
	
	private boolean objCheck(Player p1, Polygon p2) {
		Point2D po1 = p1.getLocation() ;
		Point2D po2 = p2.getLocation() ;
		
		boolean ret = (Math.abs(po1.getX() - po2.getX()) < p1.getWidth()/2 + p2.getWidth()/2) ;
		
		if(ret == true && p1.isJumping()) {
			ret = (Math.abs(po1.getY() - po2.getY()) < p1.getHeight()/2 + p2.getHeight()/2) ;
		}
		
		return ret ;
	}
	
	private boolean objCheck(Rectangle r1, Rectangle r2) {
		Point2D po1 = r1.getLocation() ;
		Point2D po2 = r2.getLocation() ;
		
		return (Math.abs(po1.getX() - po2.getX()) < r1.getWidth()/2 + r2.getWidth()/2) ;
	}
	
	private boolean objCheck(Rectangle r, Circle c) {
		Point2D po1 = r.getLocation() ;
		Point2D po2 = c.getLocation() ;
		
		return (Math.abs(po1.getX() - po2.getX()) < r.getWidth()/2 + c.getRadius()) ;
	}
	
	private boolean objCheck(Rectangle r, Polygon p) {
		Point2D po1 = p.getLocation() ;
		Point2D po2 = r.getLocation() ;
		
		return (Math.abs(po1.getX() - po2.getX()) < p.getWidth()/2 + r.getWidth()/2) ;
		
		// Doesn't work well. Commented out and reverting to default collision check.
		/*ArrayList<Line2D> e1 = p.getEdges() ;
		ArrayList<Line2D> e2 = r.getEdges() ;
		if(!(Math.abs(p.getLocation().getX() - r.getLocation().getX()) < p.getWidth()/2 + r.getWidth()/2)) return false ;
		
		for(Line2D l: e1) {
			for(Line2D l2: e2) {
				if(l.check(l2)) return true ;
			}
		}
		
		return false ;*/
	}
	
	private boolean objCheck(Circle c1, Circle c2) {
		Point2D po1 = c1.getLocation() ;
		Point2D po2 = c2.getLocation() ;
		int r1 = c1.getRadius()/2 ;
		int r2 = c2.getRadius()/2 ;
		int rSq = (int) Math.pow(r1+r2, 2) ;
		int dist = (int) (Math.pow(po1.getX()-po2.getX(), 2) + Math.pow(po1.getY()-po2.getY(), 2));
		
		return (dist <= rSq) ;
	}
	
	private boolean objCheck(Circle c, Polygon p) {
		Point2D po1 = p.getLocation() ;
		Point2D po2 = c.getLocation() ;
		
		return (Math.abs(po1.getX() - po2.getX()) < p.getWidth()/2 + c.getRadius()) ;
		
		// Doesn't work well. Commented out and reverting to default collision check.
		/*ArrayList<Line2D> ls = p.getEdges() ;
		Point2D center = c.getLocation() ;
		for(Line2D l: ls) {
			Point2D po1 = l.getP1() ;
			int rSq = (int) Math.pow(c.getRadius(), 2) ;
			int dist = (int) (Math.pow(po1.getX()-center.getX(), 2) + Math.pow(po1.getY()-center.getY(), 2));
			if(dist <= rSq) return true ;
		}
		
		return false ;*/
	}
	
	private boolean objCheck(Polygon p1, Polygon p2) {	// Should work
		ArrayList<Line2D> e1 = p1.getEdges() ;
		ArrayList<Line2D> e2 = p2.getEdges() ;
		if(!(Math.abs(p1.getLocation().getX() - p2.getLocation().getX()) < p1.getWidth()/2 + p2.getWidth()/2)) return false ;
		
		for(Line2D l: e1) {
			for(Line2D l2: e2) {
				if(l.check(l2)) return true ;
			}
		}
		
		return false ;
	}
	
	private void checkBounds(Object2D o) {
		float x = o.getBase().getXF() ;
		float y = o.getBase().getYF() ;
		float width = o.getWidth()/2 ;
		float height = o.getHeight()/2 ;
		float depth = o.getDepth() ;
		boolean flag = false ;
		
		// TODO: Pass in these values from elsewhere instead of being hardcoded
		
		if(x - width < 0) {
			flag = true ;
			x = 0 + width ;
		}
		else if(x + width > 800) {
			flag = true ;
			x = 800 - width ;
		}
		
		if(y + depth > 600) {
			flag = true ;
			y = 600 - depth;
		}
		else if(y - depth < backdropHeight) {
			flag = true ;
			y = backdropHeight + depth ;
		}
		
		if(flag) {
			o.setLocation(x, y - height) ;
		}
	}

}
